package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestSetBangIntegration(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name     string
		input    string
		expected string
	}{{
		name:     "basic set! with define",
		input:    "(define x 1)(set! x 10)x",
		expected: "10",
	},
	{
		name:     "set! with arithmetic expression",
		input:    "(define x 5)(set! x (+ x 10))x",
		expected: "15",
	},
	{
		name:     "set! with boolean",
		input:    "(define flag #t)(set! flag #f)flag",
		expected: "#f",
	},
	{ 
		name:     "set! with string",
		input:    "(define s \"hello\")(set! s \"world\")s",
		expected: "\"world\"",
	},
	{
		name:     "multiple set! operations",
		input:    "(define x 1)(set! x 2)(set! x 3)(set! x 4)x",
		expected: "4",
	},
	{
		name:     "set! inside a let block",
		input:    "(define x 10)(let ((y 20)) (set! x 30) x)",
		expected: "30",
	},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Lex
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()

			// Parse
			parser := parse.NewParser(tokens)
			program, err := parser.ParseProgram()
			if err != nil {
				t.Fatalf("Parse error: %v", err)
			}

			// Eval
			var result obj.Object
			for _, expr := range program {
				result, err = EvalExpression(expr, env)
				if err != nil {
					t.Fatalf("Eval error: %v", err)
				}
			}

			// Check result
			if result.Inspect() != tt.expected {
				t.Errorf("Expected %s, got %s", tt.expected, result.Inspect())
			}
		})
	}
}

func TestSetBangErrorHandling(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name      string
		input     string
		expectErr bool
	}{{
		name:      "set! on unbound variable",
		input:     "(set! unbound 10)",
		expectErr: true,
	},
	{
		name:      "set! with wrong number of arguments",
		input:     "(set! x)",
		expectErr: true,
	},
	{
		name:      "set! with too many arguments",
		input:     "(set! x 1 2)",
		expectErr: true,
	},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Lex
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()

			// Parse
			parser := parse.NewParser(tokens)
			program, err := parser.ParseProgram()
			if err != nil {
				if !tt.expectErr {
					t.Fatalf("Unexpected parse error: %v", err)
				}
				return
			}

			// Eval
			for _, expr := range program {
				_, err = EvalExpression(expr, env)
				if err != nil {
					if !tt.expectErr {
						t.Fatalf("Unexpected eval error: %v", err)
					}
					return
				}
			}

			if tt.expectErr {
				t.Errorf("Expected error, but no error occurred")
			}
		})
	}
}